package meta

import (
	"sync"
	"time"
	"proxy/api"
)

type Conf struct {
	Id             string
	Kind           string
	mu             sync.Mutex
	rtcMap         map[RtcId]*Rtc
	expireTime     time.Time
}

func NewConf() *Conf {
	return &Conf{
		rtcMap: make(map[RtcId]*Rtc, 0),
		expireTime: time.Now().Add(kConfTimeout),
	}
}

func (c *Conf) CountRtcByBackend() (map[int]int) {
	counts := make(map[int]int)
	c.mu.Lock()
	for id := range c.rtcMap {
		counts[id.BackendId] = counts[id.BackendId]+1
	}
	c.mu.Unlock()

	return counts
}

func (c *Conf) AddRtc(id RtcId) {
	c.mu.Lock()
	_, ok := c.rtcMap[id]
	if ok {
		c.mu.Unlock()
		return
	}
	c.rtcMap[id] = NewRtc(id.BackendId, id.RtcId)
	c.mu.Unlock()
}

func (c *Conf) DelRtc(id RtcId) bool {
	c.mu.Lock()
	delete(c.rtcMap, id)
	empty := len(c.rtcMap) == 0
	c.mu.Unlock()
	return empty
}

func (c *Conf) ListRtc() []RtcId {
	rtcIdList := make([]RtcId, 0)
	c.mu.Lock()
	for rtcId := range c.rtcMap {
		rtcIdList = append(rtcIdList, rtcId)
	}
	c.mu.Unlock()
	return rtcIdList
}

func (c *Conf) ListBackend() []int {
	backendIdList := make([]int, 0)
	backendIdEncountered := make(map[int]bool)

	c.mu.Lock()
	for rtcId := range c.rtcMap {
		if _, encountered := backendIdEncountered[rtcId.BackendId]; !encountered {
			backendIdList = append(backendIdList, rtcId.BackendId)
		}
		backendIdEncountered[rtcId.BackendId] = true
	}
	c.mu.Unlock()

	return backendIdList
}

func (c *Conf) CountRtc() int {
	c.mu.Lock()
	n := len(c.rtcMap)
	c.mu.Unlock()
	return n
}

func (c *Conf) HandleRtcTimeout() {
	expiredRtcIdList := make([]RtcId, 0)

	c.mu.Lock()

	for _, rtc := range c.rtcMap {
		if rtc.IsExpired() {
			log.Noticef("conf '%s' rtc '%d' expired", c.Id, rtc.Id.Int())
			expiredRtcIdList = append(expiredRtcIdList, rtc.Id)
		}
	}

	for _, id := range expiredRtcIdList {
		delete(c.rtcMap, id)
	}

	c.mu.Unlock()
}

func (c *Conf) Status() *api.ConfStatus {
	status := &api.ConfStatus{
		RtcMap: make(map[int]*api.RtcStatus),
	}

	c.mu.Lock()

	for _, rtc := range c.rtcMap {
		rtcStatus := rtc.Status()
		status.RtcMap[rtc.Id.Int()] = rtcStatus
	}

	c.mu.Unlock()

	return status
}

func (c *Conf) IsExpired(now time.Time) bool {
	expired := false
	c.mu.Lock()
	expired = c.expireTime.Before(now)
	c.mu.Unlock()
	return expired
}

func (c *Conf) KeepAlive(now time.Time, rtcIdList []string) {
	c.mu.Lock()

	c.expireTime = now.Add(kConfTimeout)

	for _, rtcId := range rtcIdList {
		id, err := ParseRtcId(rtcId)

		if err != nil {
			log.Warningf("keepalive: could not parse rtc id '%s'", rtcId)
			continue
		}

		//log.Debugf("keepalive: conf '%s' rtcMap %v", c.Id, c.rtcMap)

		rtc, ok := c.rtcMap[id]

		if !ok {
			rtc = NewRtc(id.BackendId, id.RtcId)
			c.rtcMap[id] = rtc
			log.Warningf("keepalive: conf '%s' restored rtc '%s'", c.Id, rtcId)
		}

		rtc.KeepAlive(now)
	}

	c.mu.Unlock()
}
//func (c *Conf) Dump() {
//	c.mu.Lock()
//
//	log.Infof("conf: '%s', kind: '%s'", c.Id, c.Kind)
//
//	for _, rtc := range c.rtcMap {
//		rtc.Dump()
//	}
//
//	c.mu.Unlock()
//}